All files / src serializer.ts

97.87% Statements 46/47
94.87% Branches 37/39
100% Functions 8/8
97.5% Lines 39/40
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96                                4x 4x     9x 9x 17x 17x     9x     4x     40x 40x 10x   30x 1x   30x     16x   14x 1x   13x 4x   9x 10x   5x 8x     1x         33x 33x 3x   30x 7x             6x 6x     6x     1x       23x 8x   20x 9x     15x   4x  
/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
const LONG_TYPE = 'type.googleapis.com/google.protobuf.Int64Value';
const UNSIGNED_LONG_TYPE = 'type.googleapis.com/google.protobuf.UInt64Value';
 
function mapValues(o: object, f: (any) => any): object {
  let result = {};
  for (var key in o) {
    Eif (o.hasOwnProperty(key)) {
      result[key] = f(o[key]);
    }
  }
  return result;
}
 
export class Serializer {
  // Takes data and encodes it in a JSON-friendly way, such that types such as
  // Date are preserved.
  encode(data: any): any {
    if (data === null || data === undefined) {
      return null;
    }
    if (data instanceof Number) {
      data = data.valueOf();
    }
    if (typeof data === 'number' && isFinite(data)) {
      // Any number in JS is safe to put directly in JSON and parse as a double
      // without any loss of precision.
      return data;
    }
    if (data === true || data === false) {
      return data;
    }
    if (toString.call(data) === '[object String]') {
      return data;
    }
    if (Array.isArray(data)) {
      return data.map(x => this.encode(x));
    }
    if (typeof data === 'function' || typeof data === 'object') {
      return mapValues(data, x => this.encode(x));
    }
    // If we got this far, the data is not encodable.
    throw new Error('Data cannot be encoded in JSON: ' + data);
  }
 
  // Takes data that's been encoded in a JSON-friendly form and returns a form
  // with richer datatypes, such as Dates, etc.
  decode(json: any): any {
    if (json === null) {
      return json;
    }
    if (json['@type']) {
      switch (json['@type']) {
        case LONG_TYPE:
        // Fall through and handle this the same as unsigned.
        case UNSIGNED_LONG_TYPE: {
          // Technically, this could work return a valid number for malformed
          // data if there was a number followed by garbage. But it's just not
          // worth all the extra code to detect that case.
          const value = parseFloat(json.value);
          Iif (isNaN(value)) {
            throw new Error('Data cannot be decoded from JSON: ' + json);
          }
          return value;
        }
        default: {
          throw new Error('Data cannot be decoded from JSON: ' + json);
        }
      }
    }
    if (Array.isArray(json)) {
      return json.map(x => this.decode(x));
    }
    if (typeof json === 'function' || typeof json === 'object') {
      return mapValues(json, x => this.decode(x));
    }
    // Anything else is safe to return.
    return json;
  }
}